import os
import requests
import py7zr
import threading
import wx
from packaging.version import Version
import logging


# 日志配置
logging.basicConfig(filename='app.log', level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')


# 下载文件
def urldownload(url, filename):
    try:
        response = requests.get(url, stream=True, timeout=10)
        response.raise_for_status()
        with open(filename, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)
        return True
    except requests.RequestException as e:
        logging.error(f"下载 {filename} 出错: {e}")
        if os.path.exists(filename):
            os.remove(filename)
        return False


# 合并7z分卷
def merge_7z_parts(part_paths, merged_path):
    try:
        with open(merged_path, 'wb') as merged_file:
            for part_path in part_paths:
                with open(part_path, 'rb') as part_file:
                    merged_file.write(part_file.read())
        return True
    except OSError as e:
        logging.error(f"合并7z分卷出错: {e}")
        if os.path.exists(merged_path):
            os.remove(merged_path)
        return False


# 解压7z文件
def extract_7z(file_path, extract_dir):
    try:
        with py7zr.SevenZipFile(file_path, 'r') as z:
            z.extractall(path=extract_dir)
        return True
    except (py7zr.Bad7zFile, py7zr.PasswordRequired) as e:
        logging.error(f"解压7z文件出错: {e}")
        return False


# 清理文件
def clean_files(file_paths):
    for file_path in file_paths:
        if os.path.exists(file_path):
            try:
                os.remove(file_path)
            except OSError as e:
                logging.error(f"删除 {file_path} 出错: {e}")


# 检测7z分卷数量
def detect_parts_count(base_url, base_name):
    part_num = 1
    while True:
        url = f"{base_url}{base_name}.7z.{str(part_num).zfill(3)}"
        try:
            response = requests.head(url, timeout=10)
            if response.status_code == 200:
                part_num += 1
            else:
                break
        except requests.RequestException:
            break
    return part_num - 1


# 下载并解压
def Download(num, filelist):
    dir_name = filelist[num].strip()
    if not os.path.isdir(dir_name):
        os.makedirs(dir_name)

    base_url = 'https://gitee.com/hzrjerry/Download/raw/master/zip/'
    part_count = detect_parts_count(base_url, dir_name)
    part_paths = []
    for i in range(1, part_count + 1):
        part_url = f"{base_url}{dir_name}.7z.{str(i).zfill(3)}"
        part_path = os.path.join(dir_name, f"{dir_name}.7z.{str(i).zfill(3)}")
        if not urldownload(part_url, part_path):
            clean_files(part_paths)
            return
        part_paths.append(part_path)

    seven_zip_merged_path = os.path.join(dir_name, f'{dir_name}.7z')
    if not merge_7z_parts(part_paths, seven_zip_merged_path):
        clean_files(part_paths + [seven_zip_merged_path])
        return

    if not extract_7z(seven_zip_merged_path, dir_name):
        clean_files(part_paths + [seven_zip_merged_path])
        return

    clean_files(part_paths + [seven_zip_merged_path])


# 显示进度对话框
def show_progress_dialog(parent, progress_type):
    dlg = wx.MessageDialog(parent, f"{progress_type} 进行中...", "提示", wx.OK | wx.ICON_INFORMATION)
    dlg.ShowModal()
    dlg.Destroy()


# 执行下载和解压
def perform_download_and_extract(num, filelist, parent):
    show_progress_dialog(parent, "下载")
    Download(num, filelist)
    show_progress_dialog(parent, "解压")


# 更新程序
def update_program(current_version, latest_version, parent):
    base_gitee_url = 'https://gitee.com/hzrjerry/Download/raw/master/versions/'
    new_version_dir = f"{base_gitee_url}{latest_version}/"
    program_file_url = new_version_dir + "Download.py"

    backup_dir = "backup_program"
    if not os.path.exists(backup_dir):
        os.makedirs(backup_dir)

    current_program_path = os.path.abspath('Download.py')
    backup_path = os.path.join(backup_dir, 'Download.py')
    with open(current_program_path, 'rb') as src, open(backup_path, 'wb') as dst:
        dst.write(src.read())

    if not urldownload(program_file_url, 'Download.py'):
        logging.error("无法下载新版本程序文件")
        dlg = wx.MessageDialog(parent, "无法下载新版本程序文件，更新失败", "错误", wx.OK | wx.ICON_ERROR)
        dlg.ShowModal()
        dlg.Destroy()
        return

    try:
        os.system(f'python {os.path.abspath("Download.py")}')
        parent.Destroy()
    except Exception as e:
        logging.error(f"启动新版本程序失败: {e}")
        dlg = wx.MessageDialog(parent, "启动新版本程序失败，更新失败", "错误", wx.OK | wx.ICON_ERROR)
        dlg.ShowModal()
        dlg.Destroy()


# 回滚程序
def rollback_program(parent):
    backup_dir = "backup_program"
    backup_path = os.path.join(backup_dir, 'Download.py')
    if not os.path.exists(backup_path):
        dlg = wx.MessageDialog(parent, "没有可回退的版本", "错误", wx.OK | wx.ICON_ERROR)
        dlg.ShowModal()
        dlg.Destroy()
        return
    with open(backup_path, 'rb') as src, open('Download.py', 'wb') as dst:
        dst.write(src.read())
    dlg = wx.MessageDialog(parent, "版本回退成功，请重新启动程序", "提示", wx.OK | wx.ICON_INFORMATION)
    dlg.ShowModal()
    dlg.Destroy()


# 主窗口
class MainFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="下载", size=(400, 600))
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)

        current_version = Version('1.0.1')
        version_url = 'https://gitee.com/hzrjerry/Download/raw/master/version.txt'
        try:
            response = requests.get(version_url, timeout=10)
            response.raise_for_status()
            latest_version_str = response.text.strip()
            latest_version = Version(latest_version_str)
        except requests.RequestException as e:
            logging.error(f"获取最新版本号出错: {e}")
            dlg = wx.MessageDialog(self, "获取最新版本号出错，程序无法继续", "错误", wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
            self.Close()
            return

        if latest_version > current_version:
            logging.info('检测到新版本，准备自动更新')
            if wx.MessageBox("有新版本可用，是否立即更新？", "提示", wx.YES_NO | wx.ICON_QUESTION) == wx.YES:
                threading.Thread(target=update_program, args=(current_version, latest_version, self)).start()
        else:
            logging.info('当前为最新版本')

        # 使用当前版本号构建下载Download.txt的网址
        version_str = str(current_version)
        download_file_url = f'https://gitee.com/hzrjerry/Download/raw/master/versions/{version_str}/Download_{version_str}.txt'
        if not urldownload(download_file_url, './Download.txt'):
            logging.error("无法下载Download.txt，程序无法继续")
            dlg = wx.MessageDialog(self, "无法下载Download.txt，程序无法继续", "错误", wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
            self.Close()
            return

        with open('Download.txt', 'r', encoding='utf8') as file:
            filelist = file.readlines()

        for i in range(len(filelist)):
            button = wx.Button(panel, label=filelist[i].strip(), size=(150, 30))
            button.Bind(wx.EVT_BUTTON, lambda event, num=i: threading.Thread(
                target=perform_download_and_extract, args=(num, filelist, self)).start())
            sizer.Add(button, 0, wx.ALL, 10)

        rollback_button = wx.Button(panel, label="回退版本", size=(150, 30))
        rollback_button.Bind(wx.EVT_BUTTON, lambda event: rollback_program(self))
        sizer.Add(rollback_button, 0, wx.ALL, 10)

        panel.SetSizer(sizer)


if __name__ == "__main__":
    app = wx.App()
    frame = MainFrame()
    frame.Show()
    app.MainLoop()